
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <apr_date.h>
#include <apr_portable.h>
#include <apr_file_io.h>
#include <apr_strings.h>
#include "../aprtools/APRFile.h"
#include "../iface/APRInterface.h"
#include "../iface/debug.h"
#include "StringUtils.h"
#include <ctype.h>

/** A wrapper for vsnprintf that calls our safe_vscnprintf function to
 * format strings.
 * 
 * @param buffer The buffer to write the string into, if necessary.
 * 
 * @param count The maximum number of characters that are allowed to
 * be output.
 * 
 * @param format The format string (printf-like).
 * 
 * @param argpntr The va_list variadic arguments list.
 * 
 * @return The number of bytes that would/are in the final string
 * representation of the given format data. */
int safewrap_vsnprintf(char *buffer, int count, const char *format, va_list argpntr)
{
   int ret = 0;
   if (format == NULL) return 0;
   if (strlen(format) == 0) return 0;
   va_list beginagain;
   va_copy(beginagain, argpntr);
   if (count == 0)
   {
      ret = StringUtils::safe_vscnprintf(NULL, format, beginagain);
   } else
   {
      ret = StringUtils::safe_vscnprintf(buffer, format, beginagain);
      if (ret > count) errprint("safewrap_vsnprintf failure (%d > %d)!", ret, count);
   }
   va_end(beginagain);
   return ret;
}

#define VSNPRINTF_COUNT_TOKEN_BUFFER_SIZE            10
#define VSNPRINTF_COUNT_FIXED_LENGTH_SYMBOL_BUFFER   (64 + VSNPRINTF_COUNT_TOKEN_BUFFER_SIZE)
#define VSNPRINTF_COUNT_TOKEN_TYPE_CHAR         0x0
#define VSNPRINTF_COUNT_TOKEN_TYPE_INT8         0x1
#define VSNPRINTF_COUNT_TOKEN_TYPE_INT16        0x2
#define VSNPRINTF_COUNT_TOKEN_TYPE_INT32        0x3
#define VSNPRINTF_COUNT_TOKEN_TYPE_INT64        0x4
#define VSNPRINTF_COUNT_TOKEN_TYPE_INT          0x5
#define VSNPRINTF_COUNT_TOKEN_TYPE_LONG         0x6
#define VSNPRINTF_COUNT_TOKEN_TYPE_LONG_LONG    0x7
#define VSNPRINTF_COUNT_TOKEN_TYPE_PTRDIFF      0x8
#define VSNPRINTF_COUNT_TOKEN_TYPE_SIZET        0x9
#define VSNPRINTF_COUNT_TOKEN_TYPE_INTMAX       0xA
#define VSNPRINTF_COUNT_TOKEN_TYPE_LONG_DOUBLE  0xB
#define VSNPRINTF_COUNT_TOKEN_TYPE_DOUBLE       0xC
#define VSNPRINTF_COUNT_TOKEN_TYPE_NULL         0xD
/** The safe_vscnprintf function to format strings. This replaces the
 * functionality of vsprintf, vsnprintf and vscprintf.
 * 
 * @param buffer The buffer to write the string into. If it is NULL
 * the function will just count the bytes it would have otherwise
 * written.
 * 
 * @param format The format string (printf-like).
 * 
 * @param argpntr The va_list variadic arguments list.
 * 
 * @return The number of bytes that would/are in the final string
 * representation of the given format data. */
int StringUtils::safe_vscnprintf(char *buffer, const char *format, va_list argpntr)
{
   // Note that we do not support %n (which breaks the spec, but, I've
   // never seen a piece of code use it).
   static const char *numeric_format_strings[] = {
      "q", "ll", "lf", "l", "I32", "I64", "Id", "Ii", "Iu", "Ix", "I", "hh", "h",
      "d", "i", "u", "e", "f", "g", "a", "x", "E", "F", "G", "A", "X", "o", "c", "L", "z", "j", "t", NULL };
   static const int numeric_format_types[] = {
      VSNPRINTF_COUNT_TOKEN_TYPE_INT64,
      VSNPRINTF_COUNT_TOKEN_TYPE_LONG_LONG,
      VSNPRINTF_COUNT_TOKEN_TYPE_DOUBLE,
      VSNPRINTF_COUNT_TOKEN_TYPE_LONG,
      VSNPRINTF_COUNT_TOKEN_TYPE_INT32,
      VSNPRINTF_COUNT_TOKEN_TYPE_INT64,
      VSNPRINTF_COUNT_TOKEN_TYPE_PTRDIFF,
      VSNPRINTF_COUNT_TOKEN_TYPE_PTRDIFF,
      VSNPRINTF_COUNT_TOKEN_TYPE_SIZET,
      VSNPRINTF_COUNT_TOKEN_TYPE_SIZET,
      VSNPRINTF_COUNT_TOKEN_TYPE_PTRDIFF,
      VSNPRINTF_COUNT_TOKEN_TYPE_INT8,
      VSNPRINTF_COUNT_TOKEN_TYPE_INT16,
      VSNPRINTF_COUNT_TOKEN_TYPE_INT,
      VSNPRINTF_COUNT_TOKEN_TYPE_INT,
      VSNPRINTF_COUNT_TOKEN_TYPE_INT,
      VSNPRINTF_COUNT_TOKEN_TYPE_DOUBLE,
      VSNPRINTF_COUNT_TOKEN_TYPE_DOUBLE,
      VSNPRINTF_COUNT_TOKEN_TYPE_DOUBLE,
      VSNPRINTF_COUNT_TOKEN_TYPE_DOUBLE,
      VSNPRINTF_COUNT_TOKEN_TYPE_INT,
      VSNPRINTF_COUNT_TOKEN_TYPE_DOUBLE,
      VSNPRINTF_COUNT_TOKEN_TYPE_DOUBLE,
      VSNPRINTF_COUNT_TOKEN_TYPE_DOUBLE,
      VSNPRINTF_COUNT_TOKEN_TYPE_DOUBLE,
      VSNPRINTF_COUNT_TOKEN_TYPE_INT,
      VSNPRINTF_COUNT_TOKEN_TYPE_INT,
      VSNPRINTF_COUNT_TOKEN_TYPE_CHAR,
      VSNPRINTF_COUNT_TOKEN_TYPE_LONG_DOUBLE,
      VSNPRINTF_COUNT_TOKEN_TYPE_SIZET,
      VSNPRINTF_COUNT_TOKEN_TYPE_INTMAX,
      VSNPRINTF_COUNT_TOKEN_TYPE_PTRDIFF,
      VSNPRINTF_COUNT_TOKEN_TYPE_NULL };
   if (format == NULL) return 0;
   int formatlen = strlen(format);
   if (formatlen == 0) return 0;
   int global_idx = 0;
   char token_buffer[VSNPRINTF_COUNT_TOKEN_BUFFER_SIZE+1];
   int token_buffer_count = 0;
   char fixedlen_buffer[VSNPRINTF_COUNT_FIXED_LENGTH_SYMBOL_BUFFER+1];
   token_buffer[0] = '\0';
   fixedlen_buffer[0] = '\0';
   if (buffer) buffer[0] = '\0';
   int total_char_count = 0;
   int evaluate_buffer_flag = 0;
   int missing_this_character = 0;
   va_list args;
   va_copy(args, argpntr);
   // Loop while still characters to parse
   while (global_idx < (formatlen+1))
   {
      switch (format[global_idx])
      {
         case '\0':
         case '%':
         {
            // Add this character to the token buffer, if this is the
            // first character in the token buffer
            if (token_buffer_count == 0)
            {
               // Add this character to the token buffer, and
               // increment the character count in the token buffer
               token_buffer[token_buffer_count++] = format[global_idx];
            } else
            {
               // This is a '%%' - this is no pointer
               if ((token_buffer_count == 1) && (token_buffer[0] == '%'))
               {
                  // Add this character to the token buffer, and
                  // increment the character count in the token buffer
                  token_buffer[token_buffer_count++] = format[global_idx];
               } else
               {
                  // set the missing character flag
                  missing_this_character = 1;
               }
               // set the token buffer evaluation flag
               evaluate_buffer_flag = 1;
            }
            break;
         }
         case '\\':
         {
            // Add this character to the token buffer, if this is the
            // first character in the token buffer
            if (token_buffer_count == 0)
            {
                // Add this character to the token buffer, and
                // increment the character count in the token buffer
                token_buffer[token_buffer_count++] = format[global_idx];
            } else
            {
               // This is a '\\' - this is no single object
               if ((token_buffer_count == 1) && (token_buffer[0] == '\\'))
               {
                  // Add this character to the token buffer, and
                  // increment the character count in the token buffer
                  token_buffer[token_buffer_count++] = format[global_idx];
               } else
               {
                  // set the missing character flag
                  missing_this_character = 1;
               }
               // set the token buffer evaluation flag
               evaluate_buffer_flag = 1;
            }
            break;
         }
         default:
         {
            // Add this character to the token buffer and increment
            // token buffer count
            token_buffer[token_buffer_count++] = format[global_idx];
            // If the token buffer is full, then evaluate the format
            // string size in the token buffer
            if (token_buffer_count == VSNPRINTF_COUNT_TOKEN_BUFFER_SIZE) evaluate_buffer_flag = 1;
            break;
         }
      }
      // If evaluate buffer flag is set, evaluate the buffer
      if (evaluate_buffer_flag)
      {
         // Flag to say 'do not consume a pointer from the list'
         int no_pointer_flag = 0;
         // null-terminate the token buffer
         token_buffer[token_buffer_count] = '\0';
         // length of token buffer
         int token_buffer_length = strlen(token_buffer);
//         printf("token buffer has: '%s' (prevtotal %d)\n",
//         token_buffer, total_char_count);
         if (token_buffer_length > 0)
         {
            // if there is no format string in this token buffer, set
            // 'do not consume a pointer'
            if (token_buffer[0] != '%') no_pointer_flag = 1;
            // if the format string is '%%' there is no pointer
            // consumed, set 'do not consume a pointer'
            if ((token_buffer_length > 1) && (token_buffer[0] == '%') && (token_buffer[1] == '%')) no_pointer_flag = 1;
            // if the format string is '%*<something>' there is no
            // pointer consumed, set 'do not consume a pointer'
            if ((token_buffer_length > 1) && (token_buffer[0] == '%') && (token_buffer[1] == '*')) no_pointer_flag = 1;
            // if this will consume a pointer...
            if (!no_pointer_flag)
            {
               int fst_typespec_idx = 0;
               // first the first alphabetic character as an indicator
               // of the type of the argument
               while (!isalpha(token_buffer[fst_typespec_idx])) fst_typespec_idx++;
               // we should now have the type of the format
               if (fst_typespec_idx < token_buffer_length)
               {
                  switch (token_buffer[fst_typespec_idx])
                  {
                     case 's':
                     {
                        // if it's a fixed length string, we have
                        // trouble...
                        if (token_buffer[1] == '.')
                        {
                           char precbuf[VSNPRINTF_COUNT_TOKEN_BUFFER_SIZE+1];
                           // get the character precision in a buffer
                           memcpy(precbuf, &(token_buffer[2]), sizeof(char)*(fst_typespec_idx - 2));
                           // null-terminate it
                           precbuf[fst_typespec_idx - 2] = '\0';
                           // get the maximum number of characters
                           int stringcounting = atoi(precbuf);
                           const char *got_stringpntr = va_arg(args, const char *);
                           if (got_stringpntr)
                           {
                              int strlength = -1;
                              int i;
                              // apparently this doesn't have to be
                              // null-terminated, so we have to walk it.
                              for (i = 0; ((i < stringcounting) && (got_stringpntr[i] != '\0')); i++);
                              // we have the total characters, increment
                              // the final count including extra
                              // characters beyond the end of the string
                              if (buffer)
                              {
                                 strncat(buffer, got_stringpntr, i);
                                 strcat(buffer, &(token_buffer[fst_typespec_idx + 1]));
                              }
                              total_char_count += (i + (strlen(token_buffer) - (fst_typespec_idx + 1)));
                           } else
                           {
                              if (buffer)
                              {
                                 strncat(buffer, "(null)", strlen("(null)"));
                                 strcat(buffer, &(token_buffer[fst_typespec_idx + 1]));
                              }
                              total_char_count += (strlen("(null)") + (strlen(token_buffer) - (fst_typespec_idx + 1)));
                           }
                        } else
                        {
                           // no precision - must be null-terminated
                           const char *got_stringpntr = va_arg(args, const char *);
                           if (got_stringpntr)
                           {
                              if (buffer)
                              {
                                 strcat(buffer, got_stringpntr);
                                 strcat(buffer, &(token_buffer[fst_typespec_idx + 1]));
                              }
                              total_char_count += (strlen(got_stringpntr) + (strlen(token_buffer) - (fst_typespec_idx + 1)));
                           } else
                           {
                              if (buffer)
                              {
                                 strcat(buffer, "(null)");
                                 strcat(buffer, &(token_buffer[fst_typespec_idx + 1]));
                              }
                              total_char_count += (strlen("(null)") + (strlen(token_buffer) - (fst_typespec_idx + 1)));
                           }
                        }
                        break;
                     }
                     // this is some other kind of thing, we assume
                     // that it's safe to print it to a fixed length
                     // buffer need to find out what it is though
                     // first to pop it off the va_list safely.
                     default:
                     {
                        // set token type to default
                        int token_type = VSNPRINTF_COUNT_TOKEN_TYPE_NULL;
                        char *eostr = &(token_buffer[fst_typespec_idx]);
                        int eostrlen = strlen(eostr);
                        int counter = 0;
                        // check to see which of the predefined format
                        // string parts that it matches
                        while (numeric_format_strings[counter] != NULL)
                        {
                           int nfsc_len = strlen(numeric_format_strings[counter]);
                           if (nfsc_len <= eostrlen)
                              if (strncmp(numeric_format_strings[counter], eostr, nfsc_len) == 0) break;
                           counter++;
                        }
                        token_type = numeric_format_types[counter];
                        switch (token_type)
                        {
                           case VSNPRINTF_COUNT_TOKEN_TYPE_CHAR:
                              sprintf(fixedlen_buffer, token_buffer, (unsigned char)va_arg(args, int)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_INT8:
                              sprintf(fixedlen_buffer, token_buffer, (uint8_t)va_arg(args, int)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_INT16:
                              sprintf(fixedlen_buffer, token_buffer, (uint16_t)va_arg(args, int)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_INT32:
                              sprintf(fixedlen_buffer, token_buffer, (uint32_t)va_arg(args, uint32_t)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_INT64:
                              sprintf(fixedlen_buffer, token_buffer, (uint64_t)va_arg(args, uint64_t)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_INT:
                              sprintf(fixedlen_buffer, token_buffer, (unsigned int)va_arg(args, int)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_LONG:
                              sprintf(fixedlen_buffer, token_buffer, (unsigned long int)va_arg(args, long int)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_LONG_LONG:
                              sprintf(fixedlen_buffer, token_buffer, (unsigned long long int)va_arg(args, long long int)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_PTRDIFF:
                              sprintf(fixedlen_buffer, token_buffer, (std::ptrdiff_t)va_arg(args, std::ptrdiff_t)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_SIZET:
                              sprintf(fixedlen_buffer, token_buffer, (size_t)va_arg(args, size_t)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_INTMAX:
                              sprintf(fixedlen_buffer, token_buffer, (uintmax_t)va_arg(args, uintmax_t)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_LONG_DOUBLE:
                              sprintf(fixedlen_buffer, token_buffer, (long double)va_arg(args, long double)); break;
                           case VSNPRINTF_COUNT_TOKEN_TYPE_DOUBLE:
                              sprintf(fixedlen_buffer, token_buffer, (double)va_arg(args, double)); break;
                           default:
                              printf("error, no type found for string '%s'\n", token_buffer); fixedlen_buffer[0] = '\0'; break;
                        }
                        if (buffer) strcat(buffer, fixedlen_buffer);
//                        printf("%s (fixedlen buffer)\n", fixedlen_buffer);
                        total_char_count += strlen(fixedlen_buffer);
                        break;
                     }
                  }
               } else errprint("no format string found at end of token block '%s'", token_buffer);
            } else
            {
               // really no pointer flag - so must be less than the
               // token buffer length really...
               sprintf(fixedlen_buffer, token_buffer);
               if (buffer) strcat(buffer, fixedlen_buffer);
               total_char_count += strlen(fixedlen_buffer);
            }
         }
         // buffer evaluation complete! - must reset everything
         fixedlen_buffer[0] = '\0';
         token_buffer[0] = '\0';
         token_buffer_count = 0;
         if (missing_this_character)
         {
            // Add this character to the token buffer, and increment
            // the character count in the token buffer
            token_buffer[token_buffer_count++] = format[global_idx];
            missing_this_character = 0;
         }
         evaluate_buffer_flag = 0;
      }
      // evaluation over, if applied, go to next character
      global_idx++;
   }
//   printf("%s (curr %d end %d)\n", token_buffer, total_char_count, token_buffer_count);
   return total_char_count;
}

/** Get the current time as an RFC822 format date/time string. The
 * caller assumes responsibility for freeing this string from the
 * pointer.
 * 
 * @return The current date and time as a string. */
char *StringUtils::getDateTimeNowAsString()
{
   char timebuf[APR_RFC822_DATE_LEN + 1];
   apr_status_t status;
   apr_time_t timet = apr_time_now();
   status = apr_rfc822_date(timebuf, timet);
   CheckAPRError(status);
   if (status != APR_SUCCESS) return NULL;
   return strdup(timebuf);
}

/** Get the current time with the given duration added to it as a
 * RFC822 format date/time string. The caller assumes responsibility
 * for freeing this string from the pointer.
 * 
 * @param secondstime How many seconds in the future this date is
 * returned as.
 * 
 * @return The future date and time as a string. */
char *StringUtils::getDateTimeNowPlusThisMuchAsString(int secondstime)
{
   char timebuf[APR_RFC822_DATE_LEN + 1];
   apr_status_t status;
   apr_time_t timet = apr_time_now() + (1000000 * secondstime);
   status = apr_rfc822_date(timebuf, timet);
   CheckAPRError(status);
   if (status != APR_SUCCESS) return NULL;
   return strdup(timebuf);
}

/** Read in a file and place it in an allocated block of memory as a
 * buffer. The caller inherits the responsibility of freeing the
 * pointer returned.
 * 
 * @param[out] fbuffer A pointer to the place when the pointer to the
 * buffer can be put.
 * 
 * @param[out] fbufferlen A pointer to an integer which is updated with the
 * length of the buffer.
 * 
 * @param filename The name of the file to load into the buffer.
 * 
 * @param mp The APR memory pool for allocating file reading
 * structures.
 * 
 * @return Zero on success, non-zero on failure. */
int StringUtils::readFileToBuffer(char **fbuffer, int *fbufferlen, const char *filename, apr_pool_t *mp)
{
   if ((filename == NULL) || (strlen(filename) == 0))
   {
      errprint("cannot read from %s filename", (filename) ? "an empty" : "a NULL");
      return -1;
   }
   if (fbuffer == NULL)
   {
      errprint("nowhere to put the output from the file '%s'", filename);
      return -1;
   }
   if (mp == NULL)
   {
      errprint("this function requires a non-NULL memory pool");
      return -1;
   }
   apr_file_t *newFP;
   apr_status_t status;
   status = APRFile::openWrapper(&newFP, filename, APR_FOPEN_BINARY|APR_FOPEN_READ|APR_FOPEN_BUFFERED, APR_FPROT_OS_DEFAULT, mp);
   CheckAPRError(status);
   if (status != 0)
   {
      errprint("could not open file '%s' for reading", filename);
      return -1;
   }
   apr_finfo_t finfo;
#ifdef USE_STAT_FILE_SIZE
   status = apr_file_info_get(&finfo, APR_FINFO_SIZE, newFP);
   CheckAPRError(status);
#else
   apr_off_t offset = 0;
   status = apr_file_seek(newFP, APR_END, &offset);
   CheckAPRError(status);
   finfo.size = offset;
   offset = 0;
   status = apr_file_seek(newFP, APR_SET, &offset);
   CheckAPRError(status);
#endif
   apr_size_t fullread = finfo.size;
   char *tbuffer = (char *)malloc(sizeof(char)*(finfo.size));
   status = apr_file_read_full(newFP, tbuffer, finfo.size, &fullread);
   CheckAPRError(status);
   if (fullread != finfo.size)
   {
      errprint("reading buffer from '%s' - expected to read %" APR_OFF_T_FMT " bytes, actually read %" APR_SIZE_T_FMT, filename, finfo.size, fullread);
      status = apr_file_close(newFP);
      CheckAPRError(status);
      free(tbuffer);
      *fbuffer = NULL;
      return -1;
   }
   status = apr_file_close(newFP);
   CheckAPRError(status);
   if (fbufferlen)
      *fbufferlen = finfo.size;
   if (fbuffer)
      *fbuffer = tbuffer;
   return 0;
}

/** Read in a file and place it in an allocated block of memory as a
 * string.  The caller inherits the responsibility of freeing the
 * pointer returned.
 * 
 * @param[out] fstring A pointer to the place when the pointer to the
 * string can be put.
 * 
 * @param filename The name of the file to load into the string.
 * 
 * @param mp The APR memory pool for allocating file reading
 * structures.
 * 
 * @return Zero on success, non-zero on failure. */
int StringUtils::readFileToString(char **fstring, const char *filename, apr_pool_t *mp)
{
   if ((filename == NULL) || (strlen(filename) == 0))
   {
      errprint("cannot read from %s filename", (filename) ? "an empty" : "a NULL");
      return -1;
   }
   if (fstring == NULL)
   {
      errprint("nowhere to put the output from the file '%s'", filename);
      return -1;
   }
   if (mp == NULL)
   {
      errprint("this function requires a non-NULL memory pool");
      return -1;
   }
   apr_file_t *newFP;
   apr_status_t status;
   status = APRFile::openWrapper(&newFP, filename, APR_FOPEN_BINARY|APR_FOPEN_READ|APR_FOPEN_BUFFERED, APR_FPROT_OS_DEFAULT, mp);
   CheckAPRError(status);
   if (status != 0)
   {
      errprint("could not open file '%s' for reading", filename);
      return -1;
   }
   apr_finfo_t finfo;
#ifdef USE_STAT_FILE_SIZE
   status = apr_file_info_get(&finfo, APR_FINFO_SIZE, newFP);
   CheckAPRError(status);
#else
   apr_off_t offset = 0;
   status = apr_file_seek(newFP, APR_END, &offset);
   CheckAPRError(status);
   finfo.size = offset;
   offset = 0;
   status = apr_file_seek(newFP, APR_SET, &offset);
   CheckAPRError(status);
#endif
   apr_size_t fullread = finfo.size;
   char *tstring = (char *)malloc(sizeof(char)*(finfo.size+1));
   status = apr_file_read_full(newFP, tstring, finfo.size, &fullread);
   CheckAPRError(status);
   tstring[fullread] = '\0';
   if (fullread != finfo.size)
   {
      errprint("reading string from '%s' - expected to read %" APR_OFF_T_FMT " bytes, actually read %" APR_SIZE_T_FMT, filename, finfo.size, fullread);
      status = apr_file_close(newFP);
      CheckAPRError(status);
      free(tstring);
      *fstring = NULL;
      return -1;
   }
   status = apr_file_close(newFP);
   CheckAPRError(status);
   *fstring = tstring;
   return 0;
}

/** Write out the contents of a buffer into an existing file.
 * 
 * @param fbuffer The buffer to write out.
 * 
 * @param fbufferlen The length of the buffer to write out.
 * 
 * @param filename The name of the file to write the buffer into.
 * 
 * @param mp The APR memory pool to use for file descriptor allocation
 * etc.
 * 
 * @return Zero on success, non-zero on failure. */
int StringUtils::overwriteFileWithBuffer(const char *fbuffer, int fbufferlen, const char *filename, apr_pool_t *mp)
{
   if (fbuffer == NULL)
   {
      errprint("writing out a NULL buffer does not make sense");
      return -1;
   }
   if ((filename == NULL) || (strlen(filename) == 0))
   {
      errprint("cannot write to %s filename", (filename) ? "an empty" : "a NULL");
      return -1;
   }
   if (mp == NULL)
   {
      errprint("this function requires a non-NULL memory pool");
      return -1;
   }
   apr_file_t *newFP;
   apr_status_t status;
   status = APRFile::openWrapper(&newFP, filename, APR_FOPEN_BINARY|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED, APR_FPROT_OS_DEFAULT, mp);
   CheckAPRError(status);
   if (status != 0)
   {
      errprint("could not open file for writing/creation/truncation");
      return -1;
   }
   apr_size_t datawritten = fbufferlen;
   int textlen = datawritten;
   status = apr_file_write_full(newFP, fbuffer, fbufferlen, &datawritten);
   CheckAPRError(status);
   if (datawritten != textlen)
   {
      errprint("writing string to '%s' - expected to write %d bytes, actually wrote %" APR_SIZE_T_FMT, filename, textlen, datawritten);
      status = apr_file_close(newFP);
      CheckAPRError(status);
      return -1;
   }
   status = apr_file_close(newFP);
   CheckAPRError(status);
   return 0;
}

/** Write out the contents of a buffer into a file.
 * 
 * @param fbuffer The buffer to write out.
 * 
 * @param fbufferlen The length fo the buffer to write out.
 * 
 * @param filename The name of the file to write the buffer into.
 * 
 * @param mp The APR memory pool to use for file descriptor allocation
 * etc.
 * 
 * @return Zero on success, non-zero on failure. */
int StringUtils::writeBufferToFile(const char *fbuffer, int fbufferlen, const char *filename, apr_pool_t *mp)
{
   if (fbuffer == NULL)
   {
      errprint("writing out a NULL buffer does not make sense");
      return -1;
   }
   if ((filename == NULL) || (strlen(filename) == 0))
   {
      errprint("cannot write to %s filename", (filename) ? "an empty" : "a NULL");
      return -1;
   }
   if (mp == NULL)
   {
      errprint("this function requires a non-NULL memory pool");
      return -1;
   }
   apr_file_t *newFP;
   apr_status_t status;
   status = APRFile::openWrapper(&newFP, filename, APR_FOPEN_CREATE|APR_FOPEN_TRUNCATE|APR_FOPEN_BINARY|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED, APR_FPROT_OS_DEFAULT, mp);
   CheckAPRError(status);
   if (status != 0)
   {
      errprint("could not open file for writing/creation/truncation");
      return -1;
   }
   apr_size_t datawritten = fbufferlen;
   int textlen = datawritten;
   status = apr_file_write_full(newFP, fbuffer, fbufferlen, &datawritten);
   CheckAPRError(status);
   if (datawritten != textlen)
   {
      errprint("writing string to '%s' - expected to write %d bytes, actually wrote %" APR_SIZE_T_FMT, filename, textlen, datawritten);
      status = apr_file_close(newFP);
      CheckAPRError(status);
      return -1;
   }
   status = apr_file_close(newFP);
   CheckAPRError(status);
   return 0;
}

/** Write out the contents of a string into a file.
 * 
 * @param fstring The string to write out.
 * 
 * @param filename The name of the file to write the string into.
 * 
 * @param mp The APR memory pool to use for file descriptor allocation
 * etc.
 * 
 * @return Zero on success, non-zero on failure. */
int StringUtils::writeStringToFile(const char *fstring, const char *filename, apr_pool_t *mp)
{
   if (fstring == NULL)
   {
      errprint("writing out a NULL string does not make sense");
      return -1;
   }
   if ((filename == NULL) || (strlen(filename) == 0))
   {
      errprint("cannot write to %s filename", (filename) ? "an empty" : "a NULL");
      return -1;
   }
   if (mp == NULL)
   {
      errprint("this function requires a non-NULL memory pool");
      return -1;
   }
   apr_file_t *newFP;
   apr_status_t status;
   status = APRFile::openWrapper(&newFP, filename, APR_FOPEN_CREATE|APR_FOPEN_TRUNCATE|APR_FOPEN_BINARY|APR_FOPEN_WRITE|APR_FOPEN_BUFFERED, APR_FPROT_OS_DEFAULT, mp);
   CheckAPRError(status);
   if (status != 0)
   {
      errprint("could not open file for writing/creation/truncation");
      return -1;
   }
   apr_size_t datawritten = strlen(fstring);
   int textlen = datawritten;
   status = apr_file_write_full(newFP, fstring, strlen(fstring), &datawritten);
   CheckAPRError(status);
   if (datawritten != textlen)
   {
      errprint("writing string to '%s' - expected to write %d bytes, actually wrote %" APR_SIZE_T_FMT, filename, textlen, datawritten);
      status = apr_file_close(newFP);
      CheckAPRError(status);
      return -1;
   }
   status = apr_file_close(newFP);
   CheckAPRError(status);
   return 0;
}

/** Format string printf-style allocation and string creation using
 * the memory pool system of APR. Variadic arguments enable printf
 * format strings to be used as-is.
 * 
 * @param mp Memory pool to allocate the new string from.
 * 
 * @param format The format string for the printed string.
 * 
 * @return The new string. The caller does not assume responsibility
 * as a memory pool is used instead. */
char *StringUtils::mpprintf(apr_pool_t *mp, const char *format, ...)
{
   va_list args;
   va_list newcopy;
   va_start(args, format); va_copy(newcopy, args);
   char *tret = StringUtils::mpprintf_va(mp, format, newcopy);
   va_end(newcopy); va_end(args);
   return tret;
}

/** Format string printf-style allocation and string creation using
 * the memory pool system of APR. Backend to the variadic call.
 * 
 * @param mp Memory pool to allocate the new string from.
 * 
 * @param format The format string for the printed string.
 * 
 * @param args Variadic argument list of data.
 * 
 * @return The new string. The caller does not assume responsibility
 * as a memory pool is used instead. */
char *StringUtils::mpprintf_va(apr_pool_t *mp, const char *format, va_list args)
{
   char room;
   va_list beginagain;
   va_copy(beginagain, args);
#ifdef USE_APR_VSNPRINTF
   int n = apr_vsnprintf(&room, 0, format, beginagain);
#else
   int n = vsnprintf(&room, 0, format, beginagain);
#endif
   va_end(beginagain);
   int m = 1;
   while (n < 0)
   {
      char *stupid_buffer = (char *)calloc(1, sizeof(char)*(m+1));
      va_copy(beginagain, args);
#ifdef USE_APR_VSNPRINTF
      n = apr_vsnprintf(stupid_buffer, m, format, beginagain);
#else
      n = vsnprintf(stupid_buffer, m, format, beginagain);
#endif
      va_end(beginagain);
      free(stupid_buffer);
      m = m * 2;
   }
   char *actualspace = (char *)apr_pcalloc(mp, sizeof(char)*(n+2));
   va_copy(beginagain, args);
#ifdef USE_APR_VSNPRINTF
   apr_vsnprintf(actualspace, n+1, format, beginagain);
#else
   vsnprintf(actualspace, n+1, format, beginagain);
#endif
   va_end(beginagain);
   return actualspace;
}

/** Format string printf-style allocation and string creation using
 * the normal malloc() call. The caller must assume responsibility for
 * properly disposing of the created string. Variadic arguments enable
 * printf format strings to be used as-is.
 * 
 * @param format The format string for the printed string.
 * 
 * @return The new string. The caller must assume responsibility for
 * the destruction of this string. */
char *StringUtils::varprintf(const char *format, ...)
{
   va_list args;
   va_list newcopy;
   va_start(args, format); va_copy(newcopy, args);
   char *tret = StringUtils::varprintf_va(format, newcopy);
   va_end(newcopy); va_end(args);
   return tret;
}

/** Format string printf-style allocation and string creation using
 * the normal malloc() call. The caller must assume responsibility for
 * properly disposing of the created string. Backend to the variadic
 * call.
 * 
 * @param format The format string for the printed string.
 * 
 * @param args Variadic argument list of data.
 * 
 * @return The new string. The caller must assume responsibility for
 * the destruction of this string. */
char *StringUtils::varprintf_va(const char *format, va_list args)
{
   char room;
   va_list beginagain;
   va_copy(beginagain, args);
#ifdef USE_APR_VSNPRINTF
   int n = apr_vsnprintf(&room, 0, format, beginagain);
#else
   int n = safewrap_vsnprintf(&room, 0, format, beginagain);
#endif
   va_end(beginagain);
   char *actualspace = (char *)calloc(1, sizeof(char)*(n+1));
   va_copy(beginagain, args);
#ifdef USE_APR_VSNPRINTF
   apr_vsnprintf(actualspace, n, format, beginagain);
#else
   safewrap_vsnprintf(actualspace, n, format, beginagain);
#endif
   va_end(beginagain);
   return actualspace;
}

/** Takes a list of string in an std::vector and disposes of each with
 * free, before deleting the container.
 * 
 * @param strlist The string list to be disposed of. */
void StringUtils::destroyStringList(std::vector<char *> *strlist)
{
   if (strlist == NULL) return;
   for (int i = 0; i < (int)(strlist->size()); i++)
      if (strlist->at(i)) free(strlist->at(i));
   delete strlist;
}

/** Takes a list of string in an std::vector and disposes of each with
 * free.
 * 
 * @param strlist The string list to be disposed of. */
void StringUtils::destroyStringList(std::vector<char *> &strlist)
{
   for (int i = 0; i < (int)(strlist.size()); i++)
      if (strlist.at(i)) free(strlist.at(i));
}

/** Takes the items in an std::vector string list and concatenates
 * them all into one big string and returns it. The caller assumes
 * responsibility for destroying the resulting string afterwards.
 * 
 * @param strlist A list of strings to flatten into one string.
 * 
 * @return The new string which is the concatenation of all the
 * strings in the list. The caller must dispose of the string
 * properly. */
char *StringUtils::flattenStringList(std::vector<char *> *strlist)
{
   return flattenStringList(strlist, NULL);
}

/** Takes the items in an std::vector string list and concatenates
 * them all into one big string with separators and returns it.  The
 * caller assumes responsibility for destroying the resulting string
 * afterwards.
 * 
 * @param strlist A list of strings to flatten into one string.
 * 
 * @param sep A seperator string to put between the strings in the
 * string list when concatenating them together.
 * 
 * @return The new string which is the concatenation of all the
 * strings in the list. The caller must dispose of the string
 * properly. */
char *StringUtils::flattenStringList(std::vector<char *> *strlist, const char *sep)
{
   int total_length = 0;
   for (int i = 0; i < (strlist->size()); i++)
   {
      total_length += strlen(strlist->at(i));
      total_length += (((sep) && (i < (strlist->size()-1))) ? strlen(sep) : 0);
   }
   int total_length2 = 0;
   char *cbuffer = (char *)malloc(sizeof(char)*(total_length+1));
   for (int i = 0; i < (strlist->size()); i++)
   {
      int thissize = strlen(strlist->at(i));
      memcpy(&(cbuffer[total_length2]), strlist->at(i), sizeof(char)*thissize);
      total_length2 += thissize;
      if ((sep) && (i < (strlist->size()-1)))
      {
         int thissize2 = strlen(sep);
         memcpy(&(cbuffer[total_length2]), sep, sizeof(char)*thissize2);
         total_length2 += thissize2;
      }
   }
   cbuffer[total_length] = '\0';
   return cbuffer;
}

/** Unflattens a string list. Takes a separated string and extracts
 * a list of the separated strings.
 * 
 * @param strlist The list of strings, separated by for example commas.
 * 
 * @param sep A string of separators.
 * 
 * @return A std::vector of strings of the separate string without the 
 * separators. */
std::vector<char *> *StringUtils::unflattenStringList(const char *strlist, const char *sep)
{
   if ((strlist == NULL) || (strlen(strlist) == 0)) return NULL;
   std::vector<char *> *newstrlist = new std::vector<char *>();
   char *strlist_copy = strdup(strlist);
   char *remainder = strlist_copy;
   char *token = strlist_copy;
   while (token != NULL)
   {
      token = apr_strtok(NULL, sep, &remainder);
      if (token) newstrlist->push_back(strdup(token));
   }
   free(strlist_copy);
   return newstrlist;
}

/** Sum a set of separated (unsigned) numbers.
 * 
 * @param numlist The list of separated (unsigned) numbers.
 * 
 * @param sep The string of spearator characters used in the list.
 * 
 * @return The sum of the (unsigned) numbers. */
int64_t StringUtils::sumNumberList(const char *numlist, const char *sep)
{
   if ((numlist == NULL) || (strlen(numlist) == 0)) return 0;
   std::vector<char *> *numlist_list = unflattenStringList(numlist, sep);
   if (numlist_list == NULL) return 0;
   int64_t total = 0;
   for (int i = 0; i < numlist_list->size(); i++)
   {
      char *pv = numlist_list->at(i);
      int value = atoi(pv);
      if (pv)
         total += ((value < 0) ? 0 : value);
   }
   destroyStringList(numlist_list);
   return total;
}

/** Sanitise a string in-place by replacing characters via this lookup
 * table. Anything in the string given that is not in the lookup table
 * is replaced with an underscore (_).
 * 
 * @param[in,out] tstring String to sanitise in-place. */
void StringUtils::aggressiveStringSanitiser(char *tstring)
{
   const char modascii[96] = { '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '_', '+', '_', '-', '.', '_',
                               '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', '_', '_', '_', '_', '_',
                               '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
                               'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_', '_', '_', '_', '_',
                               '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
                               'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '_', '_', '_', '_', '_' };
   int stlen = strlen(tstring);
   for (int i = 0; i < stlen; i++)
      tstring[i] = (tstring[i] > 32) ? modascii[tstring[i] - 32] : '_';
}

/** Intersect two comma-separated lists and return the comma-separated
 * list that contains only the common elements between them. This
 * returned list must be freed by the caller later.
 * 
 * @param csl1 The first comma-separated list.
 * 
 * @param csl2 The second comma-separated list.
 * 
 * @return A new list, that contains the elements that both lists have
 * in common. The onus for freeing this lies with the caller. */
char *StringUtils::intersectCSL(const char *csl1, const char *csl2)
{
   if ((csl1 == NULL) || (strlen(csl1) == 0)) return NULL;
   if ((csl2 == NULL) || (strlen(csl2) == 0)) return NULL;
   char *tcsl1 = (char *)malloc(sizeof(char)*(strlen(csl1)+1));
   strcpy(tcsl1, csl1);
   char *tcsl2 = (char *)malloc(sizeof(char)*(strlen(csl2)+1));
   strcpy(tcsl2, csl2);
   char *varplist = (char *)malloc(sizeof(char));
   varplist[0] = '\0';
   char *remainder1 = NULL;
   char *remainder2 = NULL;
   char *ttoken1 = apr_strtok(tcsl1, ",", &remainder1);
   while (ttoken1 != NULL)
   {
      strcpy(tcsl2, csl2);
      char *ttoken2 = apr_strtok(tcsl2, ",", &remainder2);
      while (ttoken2 != NULL)
      {
         if (strcmp(ttoken1, ttoken2) == 0)
         {
            char *oldvarplist = varplist;
            varplist = StringUtils::varprintf((strlen(oldvarplist) > 0) ? "%s,%s" : "%s%s", oldvarplist, ttoken1);
            free(oldvarplist);
            break;
         }
         ttoken2 = apr_strtok(NULL, ",", &remainder2);
      }
      ttoken1 = apr_strtok(NULL, ",", &remainder1);
   }
   free(tcsl1);
   free(tcsl2);
   return varplist;
}

/** Find the differences between two comma-separated lists and render
 * these differences as a comma-separated list that contains the
 * elements that are not in either list. This list must be freed by
 * the caller.
 * 
 * @param csl1 The first comma-separated list.
 * 
 * @param csl2 The second comma-separated list.
 * 
 * @return The list of all elements that are not in either list. The
 * list that is returned must be later freed by the caller. */
char *StringUtils::differenceCSL(const char *csl1, const char *csl2)
{
   char *varplist = (char *)malloc(sizeof(char));
   varplist[0] = '\0';
   if ((csl1 == NULL) || (strlen(csl1) == 0))
   {
      if (csl2 == NULL)
         return NULL;
      else
      {
         char *oldvarplist = varplist;
         varplist = StringUtils::varprintf((strlen(oldvarplist) > 0) ? "%s,%s" : "%s%s", oldvarplist, csl2);
         free(oldvarplist);
      }
      return varplist;
   }
   if ((csl2 == NULL) || (strlen(csl2) == 0))
   {
      if (csl1 == NULL)
         return NULL;
      else
      {
         char *oldvarplist = varplist;
         varplist = StringUtils::varprintf((strlen(oldvarplist) > 0) ? "%s,%s" : "%s%s", oldvarplist, csl1);
         free(oldvarplist);
      }
      return varplist;
   }
   char *tcsl1 = (char *)malloc(sizeof(char)*(strlen(csl1)+1));
   strcpy(tcsl1, csl1);
   char *tcsl2 = (char *)malloc(sizeof(char)*(strlen(csl2)+1));
   strcpy(tcsl2, csl2);
   char *remainder1 = NULL;
   char *remainder2 = NULL;
   char *ttoken1 = apr_strtok(tcsl1, ",", &remainder1);
   while (ttoken1 != NULL)
   {
      strcpy(tcsl2, csl2);
      char *ttoken2 = apr_strtok(tcsl2, ",", &remainder2);
      int all_nonmatching = 1;
      while (ttoken2 != NULL)
      {
         if (strcmp(ttoken1, ttoken2) == 0) all_nonmatching = 0;
         ttoken2 = apr_strtok(NULL, ",", &remainder2);
      }
      if (all_nonmatching)
      {
         char *oldvarplist = varplist;
         varplist = StringUtils::varprintf((strlen(oldvarplist) > 0) ? "%s,%s" : "%s%s", oldvarplist, ttoken1);
         free(oldvarplist);
      }
      ttoken1 = apr_strtok(NULL, ",", &remainder1);
   }
   free(tcsl1);
   free(tcsl2);
   return varplist;
}

/** This is a wrapper around strcmp that allows string comparison
 * which enables quicksorting with the c standard librar function
 * 'qsort'.
 * 
 * @param c1 First string to compare as a void pointer.
 * 
 * @param c2 Second string to compare as a void pointer.
 * 
 * @return An integer signifying ordinal direction. */
int StringUtils::qsort_strcmp_wrapper(const void *c1, const void *c2)
{
   return strcmp(*((char **)c1), *((char **)c2));
}

/** A check to see if the capabilities offered by the compute are
 * enough to satisfy the requirements of the work.
 * 
 * @param capswork The comma-separated list of capabilities required
 * by the work.
 * 
 * @param capscompute The comma-separated list of capabilities
 * advertised by the compute.
 * 
 * @return Non-zero for true/success (so capabilities are acceptable),
 * zero for false/failure (capabilities unacceptable). */
int StringUtils::areCapabilitiesAcceptable(char *capswork, char *capscompute)
{
   // Get intersection of two lists
   // Build new list1 from the things not in the intersection
   // Build new list2 from the things not in the intersection
   // Sort both lists alphabetically.
   // Step through the lists making sure that list1 is a subset of list2
   // If so, accept possibility, if not reject
   char *isect = intersectCSL(capswork, capscompute);
   char *diff_work = differenceCSL(capswork, isect);
   char *diff_compute = differenceCSL(capscompute, isect);
   char *ttoken, *remainder;
   free(isect);
   if ((diff_work == NULL) || (diff_compute == NULL))
   {
      if (diff_work) free(diff_work);
      if (diff_compute) free(diff_compute);
      return (!((diff_work != NULL) || (diff_compute == NULL)));
   }
   remainder = NULL;
   std::vector<char *> *capswork_diff = new std::vector<char *>();
   ttoken = apr_strtok(diff_work, ",", &remainder);
   while (ttoken != NULL)
   {
      capswork_diff->push_back(ttoken);
      ttoken = apr_strtok(NULL, ",", &remainder);
   }
   if (capswork_diff->size() == 0)
   {
      free(diff_work);
      free(diff_compute);
      delete capswork_diff;
      return 1;
   }
   remainder = NULL;
   std::vector<char *> *capscompute_diff = new std::vector<char *>();
   ttoken = apr_strtok(diff_compute, ",", &remainder);
   while (ttoken != NULL)
   {
      capscompute_diff->push_back(ttoken);
      ttoken = apr_strtok(NULL, ",", &remainder);
   }
   qsort(&(capswork_diff->at(0)), capswork_diff->size(), sizeof(char *), StringUtils::qsort_strcmp_wrapper);
   qsort(&(capscompute_diff->at(0)), capscompute_diff->size(), sizeof(char *), StringUtils::qsort_strcmp_wrapper);
   // We know that there is nothing the same between these two lists
   // Begin on the same index
   int *possiblefailures = (int *)malloc(sizeof(int)*capswork_diff->size());
   for (int i = 0; i < capswork_diff->size(); i++) possiblefailures[i] = 1;
   int i = 0, j = 0;
   for ( ; ((i < capswork_diff->size()) && (j < capscompute_diff->size())) ; )
   {
      // First, copy both
      char *work_candidate = (char *)malloc(sizeof(char)*(strlen(capswork_diff->at(i))+1));
      strcpy(work_candidate, capswork_diff->at(i));
      char *compute_candidate = (char *)malloc(sizeof(char)*(strlen(capscompute_diff->at(j))+1));
      strcpy(compute_candidate, capscompute_diff->at(j));
      char *rem_work = NULL;
      char *key_work = apr_strtok(work_candidate, "=", &rem_work);
      char *value_work = apr_strtok(NULL, "=", &rem_work);
      char *rem_compute = NULL;
      char *key_compute = apr_strtok(compute_candidate, "=", &rem_compute);
      char *value_compute = apr_strtok(NULL, "=", &rem_compute);
      if (strcmp(key_work, key_compute) == 0)
      {
         // Only succeed if the work demands less or equal to the
         // resource that the compute can provide!
         if (atoi(value_work) <= atoi(value_compute))
         {
            possiblefailures[i] = 0;
         }
      }
      int tval = strcmp(capswork_diff->at(i), capscompute_diff->at(j));
      (tval > 0) ? j++ : i++;
      free(work_candidate);
      free(compute_candidate);
   }
   int wasfailure = 0;
   for (int i = 0; i < capswork_diff->size(); i++)
      wasfailure |= possiblefailures[i];
   free(diff_work);
   free(diff_compute);
   delete capswork_diff;
   delete capscompute_diff;
   return (!(wasfailure));
}

/** Destroy a string completely.
 * 
 * @param stringpntr A pointer to the string pointer to be
 * destroyed. */
void StringUtils::razeString(char **stringpntr)
{
   char *oldstring = *stringpntr;
   if (oldstring == NULL) return;
   memset(oldstring, 0, sizeof(char)*strlen(oldstring));
   *stringpntr = NULL;
   free(oldstring);
}

/** Replace malloc-allocated string with a string allocated from the
 * APR memory pool. The old string is freed here.
 * 
 * @param oldstring The string allocated from malloc which is
 * destroyed by this function.
 * 
 * @param mp The memmory pool from which to get more memory and create
 * a new string such that it gets destroyed with the memory pool and
 * doesn't leak.
 * 
 * @return The new string allocated from the memory pool. */
char *StringUtils::replaceWithPoolString(char *oldstring, apr_pool_t *mp)
{
   if (oldstring == NULL) return NULL;
   char *newstring = apr_pstrdup(mp, oldstring);
   free(oldstring);
   return newstring;
}

/** Heuristic to check whether a given buffer is binary (and should be
 * displayed using the binary printing method).
 * 
 * @param bindata The buffer to check.
 * 
 * @param length The length of the buffer to check.
 * 
 * @return True if this is binary data, false if this is text. */
int StringUtils::isBinary(const char *bindata, int length)
{
   int isbindata = 0;
   for (int k = 0; k < length; k++) isbindata |= (bindata[k] < '\0');
   return isbindata;
}

/** Print nicely formatted binary data.
 * 
 * @param bindata The binary data to print.
 * 
 * @param length The length of data to print. */
void StringUtils::printBinary(const char *bindata, int length)
{
   for (int k = 0x00; k <= length; k+=0x10)
   {
      printf("0x%.5X:  ", k);
      for (int j = 0x00; j < 0x10; j++)
      {
         if ((k+j) < length)
            printf("%.2X ", (unsigned char)(bindata[k+j]));
         else
            printf("   ");
         if ((j & 0x3) == 0x3) printf(" ");
      }
      for (int j = 0x00; j < 0x10; j++)
      {
         if ((k+j) < length)
            printf("%c", ((bindata[k+j] < ' ') ? '.' : bindata[k+j]));
         else
            printf(" ");
      }
      printf("\n");
   }
}

/** Filter out white space by duplicating via a memory pool.
 * 
 * @param original
 * The original string to trim whitespace on both sides with.
 * 
 * @param mp
 * The memory pool from which to allocate the new string.
 * 
 * @return
 * A new string, allocated from the memory pool with no left
 * or right side whitespace remaining. */
const char *StringUtils::mpTrimWhitespaceFromBoth(char *original, apr_pool_t *mp)
{
   if ((original == NULL) || (strlen(original) == 0)) return NULL;
   char *newstring = apr_pstrdup(mp, original);
   for (int i = (strlen(original) - 1); i >= 0; i--)
   {
      if (newstring[i] <= ' ')
         newstring[i] = '\0';
      else
         break;
   }
   int j = 0;
   for (j = 0; j < strlen(original); j++)
   {
      if (newstring[j] <= ' ')
         newstring[j] = '\0';
      else
         break;
   }
   return (const char *)(&(newstring[j]));
}
